﻿using System;
using System.Linq;
using System.Security.Cryptography;
using housingsafety.server.Caching.Core;
using housingsafety.server.Entities;
using housingsafety.server.Entities.DataModels;
using housingsafety.server.Helpers;
using housingsafety.server.Models;
using housingsafety.server.Repository;
using housingsafety.server.Security;
using housingsafety.server.Svcs;
using Microsoft.Extensions.Options;

namespace housingsafety.server.SvcImpls
{
    /// <summary>
    /// 用户服务实现
    /// </summary>
    public class UserTokenService : IUserTokenService
    {
        private readonly ICache _cache;
        private readonly IRepository<ZmUser> _userRepository;

        private readonly IRepository<ZmBuildings> _buildingRepository;
        private readonly IRepository<ZmLoginLog> _loginRepository;
        private readonly IOptions<TokenManagement> _tokenManagement;

        /// <summary>
        /// 构造
        /// </summary>
        /// <param name="userRepository"></param>
        /// <param name="loginRepository"></param>
        /// <param name="tokenManagement"></param>
        /// <param name="cacheProvider"></param>
        public UserTokenService(IRepository<ZmBuildings> buildingRepository,
            IRepository<ZmUser> userRepository
            , IRepository<ZmLoginLog> loginRepository
            , IOptions<TokenManagement> tokenManagement
            , ICacheProvider cacheProvider)
        {
            _userRepository = userRepository;
            _loginRepository = loginRepository;
            _tokenManagement = tokenManagement;
            _cache = cacheProvider.CreateCache();
            _buildingRepository = buildingRepository;
        }

        /// <summary>
        /// 验证用户
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ipAddress"></param>
        /// <returns></returns>
        public RespRet<string> ValidateUser(AuthenticateRequest model, string ipAddress)
        {
            if (null == model)
            {
                return new RespRet<string>(false, "请输入用户名和密码");
            }
            if (string.IsNullOrWhiteSpace(model.UserName))
            {
                return new RespRet<string>(false, "请输入用户名");
            }
            if (string.IsNullOrWhiteSpace(model.Pwd))
            {
                return new RespRet<string>(false, "请输入密码");
            }
            var user = _userRepository.Query(u => u.Uname == model.UserName && u.IsDelete != 1&&u.Disabled!=1).FirstOrDefault();
            if (null == user)
            {
                return new RespRet<string>(false, "用户不存在");
            }

            if (user.Disabled.HasValue && user.Disabled.Value == 1)
            {
                return new RespRet<string>(false, "您的账号已被冻结");
            }

            //取消MD5加盐
            //var pwdmd5 = (model.Pwd + user.Salt).Md5();
            var pwdmd5 = (model.Pwd).Md5();
            if (!user.Pwd.Equals(pwdmd5))
            {
                return new RespRet<string>(false, "密码错误");
            }

            user.ModifiedIp = ipAddress;
            user.LastLoginIp = ipAddress;
            user.LastLoginOn = DateTime.Now;
            user.ModifiedOn = user.LastLoginOn;
            user.LoginCount = user.LoginCount + 1 ?? 1;

            _userRepository.Update(user);
            _userRepository.SaveChanges();

            var dmLoginLog = new ZmLoginLog();
            dmLoginLog.Id = SequentialGuid.SequentialGuidGenerator.Instance.NewGuid().ToString("N");
            dmLoginLog.CreateBy = user.Number;
            dmLoginLog.CreateIp = ipAddress;
            dmLoginLog.CreateOn = user.LastLoginOn.Value;

            _loginRepository.Insert(dmLoginLog);
            _loginRepository.SaveChanges();

            _cache.Set(ConstRedisKeys.UserRoles(user.Number), new UserRoleSet(user.Number, user.SysAdmin == 1));

            return new RespRet<string>(true, "Ok", user.Number);
        }



        /// <summary>
        /// 验证用户
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ipAddress"></param>
        /// <returns></returns>
        public RespRet<string> ValidateUserId(ZmUser user, string ipAddress)
        {


            if (user.Disabled.HasValue && user.Disabled.Value == 1)
            {
                return new RespRet<string>(false, "您的账号已被冻结");
            }
            user.ModifiedIp = ipAddress;
            user.LastLoginIp = ipAddress;
            user.LastLoginOn = DateTime.Now;
            user.ModifiedOn = user.LastLoginOn;
            user.LoginCount = user.LoginCount + 1 ?? 1;

            _userRepository.Update(user);
            _userRepository.SaveChanges();
            var dmLoginLog = new ZmLoginLog();
            dmLoginLog.Id = SequentialGuid.SequentialGuidGenerator.Instance.NewGuid().ToString("N");
            dmLoginLog.CreateBy = user.Number;
            dmLoginLog.CreateIp = ipAddress;
            dmLoginLog.CreateOn = user.LastLoginOn.Value;
            _loginRepository.Insert(dmLoginLog);
            _loginRepository.SaveChanges();
            _cache.Set(ConstRedisKeys.UserRoles(user.Number), new UserRoleSet(user.Number, user.SysAdmin == 1));

            return new RespRet<string>(true, "Ok", user.Number);
        }



        /// <summary>
        /// 验证房屋编号
        /// </summary>
        /// <param name="model"></param>
        /// <param name="ipAddress"></param>
        /// <returns></returns>
        public RespRet<string> ValidateUserFwbh(string fwbh, string ipAddress)
        {
            if (string.IsNullOrWhiteSpace(fwbh))
            {
                return new RespRet<string>(false, "房屋编号不能为空");
            }

            var user = _buildingRepository.Query(u => u.Fwbh == fwbh && u.IsDelete != 1).FirstOrDefault();
            if (null == user)
            {
                return new RespRet<string>(false, "房屋编号不正确");
            }

            //取消MD5加盐
            //var pwdmd5 = (model.Pwd + user.Salt).Md5();
            var pwdmd5 = (fwbh).Md5();
            var dmLoginLog = new ZmLoginLog();
            dmLoginLog.Id = SequentialGuid.SequentialGuidGenerator.Instance.NewGuid().ToString("N");
            dmLoginLog.CreateBy = fwbh;
            dmLoginLog.CreateIp = ipAddress;
            dmLoginLog.CreateOn = DateTime.Now;

            _loginRepository.Insert(dmLoginLog);
            _loginRepository.SaveChanges();

            _cache.Set(ConstRedisKeys.UserRoles(fwbh), new UserRoleSet(fwbh,false ));

            return new RespRet<string>(true, "Ok", fwbh);
        }


        /// <summary>
        /// 吊销token
        /// </summary>
        /// <param name="refreshTokenOrUserNumber"></param>
        public void RevokeToken(string refreshTokenOrUserNumber)
        {
            string key = ConstRedisKeys.RefreshTokens(GetUserNumber(refreshTokenOrUserNumber));
            _cache.Remove(key);
        }

        private string GetUserNumber(string refreshTokenOrUserNumber)
        {
            return TokenAuthenticationService.GetUserNumber(refreshTokenOrUserNumber);
        }

        /// <summary>
        /// 刷新token
        /// </summary>
        /// <param name="refreshTokenOrUserNumber"></param>
        /// <returns></returns>
        public RespRet<UserRefreshToken> GetRefreshTokens(string refreshTokenOrUserNumber)
        {
            string key = ConstRedisKeys.RefreshTokens(GetUserNumber(refreshTokenOrUserNumber));
            UserRefreshToken userRefreshToken = _cache.Get<UserRefreshToken>(key);
            return new RespRet<UserRefreshToken>(null != userRefreshToken, null != userRefreshToken ? "Ok" : "refreshToken do not exists", userRefreshToken);
        }

        /// <summary>
        /// 更新token
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public RespRet UpdateRefreshTokens(UserRefreshToken user)
        {
            var cloneUser = user.Clone();
            TimeSpan ts = TimeSpan.FromMinutes(_tokenManagement.Value.RefreshExpiration);
            CacheEntryOptions options = CacheEntryOptions.FromAbsoluteExpirationRelativeToNow(ts);
            string key = ConstRedisKeys.RefreshTokens(user.Id);
            _cache.Set(key, cloneUser, options);
            return new RespRet(true, "Ok");
        }
    }
}